Skip to main content

Notiuni de baza

Pentru majoritatea limbajelor de programare, fie ca sunt Java, Python, sau Blocks, sunt cateva concepte care se aplica în fiecare dintre aceste limbaje. Aceste idei sunt baza atunci când înveți sa programezi, aplicandu-le nu numai în Programarea FTC dar și în general.

Aceasta sectiune este dedicata incepatorilor în programare. Chiar și daca ai experienta anterioara, tot poate sa iti fie de ajutor sa arunci o privire asupra acestei sectiuni, avand la dispozitie concepte pe care inca nu le-ai abordat.

Exemplele for fi în mare parte în Java, unde // indica un comentariu pe care programul il ignora și este folosit de persoane pentru a adauga comentarii aditionale/explicative.

Main.java
int numar; // Declararea unui numar care va contine o valoare naturala
numar = 5; // Atribuirea unei valori variabilei

int aldoileaNr = 6; // Aplicarea inscructiunilor de mai sus simultan

int total = numar + aldoileaNumar; // Matematica :)
System.out.println(total); // Afisare, va aparea numarul 11 (total).

  • Daca nu am setat o valoare pentru un numar și am incercat sa il afisez, ce va afisa?
  • Ce alte opreatii pot sa fac cu variabilele de mai sus?
  • Pot sa setez un numar zecimal? Daca nu, ce s-ar intampla?
  • Sterge un caracter din cod. Tine minte eroarea(daca este), apoi fixeaz-o. Sterge alta parte. Cate erori diferite vei primii? //posibil sa nu avem nevoie

Tipuri de date (variabile)

  • Numere -> Natural(int), real(float/double), Boolean (True/False)
  • Strings -> Siruri de caractere(String)
  • Altele: (Array, Hasmap, HashSet,etc) - depinde de limbaj
  • Scopul acestor tipuri în declararea lor este de a ii usura munca calculatorului prin punerea bazei, zicandu-i ce sa faca cu acea variabila și modul în care poate fi folosita.

Exemplu:

rtc.java
String Nume = “RTC”;

String descriere = “ este very coquette”;

//afisare

System.out.println(Nume + descriere);

//Metoda pe care am folosit-o pentru a afisa variabilele se numeste Concatenare a Sirurilor de caractere ( sau String Concatenation mai simplu :D )

Cateva provocari pentru a exersa

  • Incearca sa inlocuiesti variabila nume cu altceva. Numele tau, numele animalului tau de companie, sad hamster, orice!!
  • Incearca sa adaugi un numar sau un String, ce se va intampla? 🤨
  • Crezi ca poti adauga mai multe String-uri și numere? Incearca!!! :D

Structuri de control al fluxului (foarte importante!)

  • Cateva dintre cele mai importante lucruri în a iti asigura o baza stabila în programare sunt structurile de control al fluxului. Stim, suna foarte fancy, insa sunt defapt niste concepte simple care te ajuta sa scrii cod intr-un mod eficient :). Sunt folosite nu numai în programarea FTC, dar cam peste tot. Asadar, ai grija sa stii ce sunt , cum functiuneaza și cum/cand sa le folosesti, sunt foarte importante!!

Structuri de date liniare

  • Structurile de data liniare sunt o metoda de iti organiza și a stoca seturi de date mari. Sunt multe tipuri de date liniare, care difera în functie de contextul folosirii acestora. Pentru mai multe informatii, iti recomandam sa consulti ghidul nostru de programare, insa nu vom acoperii tot, asa ca ar fi bine sa te documentezi în plus.
  • Array-urile:
    • Acestea sunt tipurile de date liniare de baza (și cele mai folosite). Atunci cand initializam un array, marimea sa va trebui setata la initializare, și nu va putea fi schimbata, de aceea va trebui sa nu asiguram ca alocam suficient spatiu pentru array de la inceput.
    • Pentru a extinde spatiul alocat, va trebui sa creezi un nou array cu marimea dorita, iar mai apoi sa copiezi array ul initial în array ul nou. De asemenea, elementele sunt stocate, din punct de vedere al pozitiei, adiacent unul fata de altul.
    • O alta observatie importanta, chiar daca accesarea elementelor dintr-un array are o complexitate liniara ( O(n) ), datorita modului în care accesarea unui element este calculata, folosindu-se de bitset-ul din array, acesta este foarte eficient la a citi date.

LinearOpMode V.S OpMode

  • Inainte sa continuam, ar trebui sa aflam mai intai ce este un opmode, totusi?
  • Un opMode, teoretic, este o clasa Localizata în SDK-ul FTC. Cu ce ne ajuta? Well, datorita acestei clase, putem sa declaram și sa adaugam codul în controller-ul robotului.
  • Diferentele principale intre LOPM și OPM constau în metodele pe care acestea le au.

Asadar, acestea sunt diferentele dintre ele:

LinearOpMode

  • runOpMode() : codul aflat în aceasta metoda va rula atunci cand vei apasa pe butonul INIT. Aici vei pune tot codul pentru OpMode.
  • waitForStart(): Aceasta metoda pune pauza la OpMode pana cand apesi butonul de START de pe driver station
  • isStarted(): returneaza true daca butonul de START a fost apasat, altfel returneaza false.
  • idle(): apeleaza Thread.yield, permitandu-le altor threaduri care au aceeasi prioritate sa ruleze.
  • opModeIsActive(): returneaza isStarted() && !isStopRequested() și apeleaza idle();
  • opModeInInit(): returneaza !isStarted() && !isStopRequested() și nu apeleaza idle().

OpMode

  • init_loop(): Atunci cand codul din init() a fost rulat, codul din aceasta metoda va runa continuu pana cand butonul de START de pe driver station este apasat.
  • start(): Codul din aceasta metoda va rula exact dupa ce apesi butonul de START de pe driver station.
  • loop(): Atunci cand codul din start() a fost rulat, codul din aceasta metoda va rula continuu pana cand butonul de STOP este apasat.
  • stop(): Codul din aceasta metoda va rula exact dupa ce apresi butonul de STOP de pe driver station.

În concluzie, utilizarea unui OpMode simplu sau unui LinearOpMode depinde de preferintele fiecaruia, insa în acest game manual folosim în principal LinearOpMode.

Citirea și scrierea pe hardware

  • Atunci cand folosim SDK-ul FTC, avem diverse varietati de clase preprogramate pentru hardware, pe care le putem folosi pentru a comunica cu partea de hardware de pe robot(de ex DC, motoare, Servo și alti senzori), prin intermediului programului.

Creearea și instantarea Obiectelor pentru Hardware

Primul lucru pe care trebuie sa facem este sa importam clasa. În Android Studio, daca clasa este mentionata fara sa fie importata, incearca sa apesi Alt+Enter pentru a o importa automat. Dupa ce a fost importata, urmatorul pas este sa creeam obiectul:

dcmotor.java
private DcMotor liftMotor;

Dupa ce am creat obiectul, trebuie sa fie initializat. O parte din superclasa OpMode care ne va ajuta se numeste hardwaremap. HardwareMap este folosit în FTC SDK pentru a initializa obiecte.

Contine toate infromatiile introdu-se în configurarea Controllerului al Robotului, cum ar numele hardware-ului și în ce port este conectat. Exemplu de creare a unui instante al unui motor pe care l-am creat mai sus.

dcmotor.java
liftMotor = hardwaremap.get(DcMotor.class, “Lift Motor”);

Indiferent de senzorul pe care il folosesti, vei pune senzorul/motorul respectiv în clasa, spre exemplu, daca am fi avut un servo, atunci am fi pus Servo.class.

În al doilea argument, trebuie sa pui numele device-ului, exact asa cum este configurat în Robot Controller. Atunci cand apelam metoda, hardwaremap o sa gaseasca portul cu acel nume, ceea ce ne va permite sa accesam hardware-ul respectiv.

Exemple de folosire a componentelor Hardware

DCMotor

dcmotor.java
DcMotor leftMotor **=** hardwareMap**.**get**(**DcMotor**.**class**,** "Left Motor"**);**

DcMotor rightMotor **=** hardwareMap**.**get**(**DcMotor**.**class**,** "Right Motor"**);**

DcMotor elevatorMotor **=** hardware**.**get**(**DcMotor**.**class**,** "Elevator Motor"**);**

DcMotor intakeMotor **=** hardware**.**get**(**DcMotor**.**class**,** "Intake Motor"**);**

Dupa ce instantam un DcMotor, sunt cateva variabile pe care trebuie sa le declaram care controleaza cum functioneaza DcMotor-ul.

Primul este directia:

dcmotor.java
leftMotor**.**setDirection**(**DcMotor**.**Direction**.**REVERSE**);**

rightMotor**.**setDirection**(**DcMotor**.**Direction**.**FORWARD**);**

Schimbarea directiei motorului face exact ce ai crede ca face, schimba directia :) . Daca aplicam o putere de 1 pe motor cat timp este în modul forward, o sa se roteasca la o directie. Daca este în modul opus, o putere de 1 il va rotii în cealalta directie. Daca esti cu fata axului catre tine, miscarea inainte va fi în sensul opus al acelor de ceasornic.

În continuare, avem 2 moduri de putere la 0:

dcmotor.java
leftMotor**.**setZeroPowerBehavior**(**DcMotor**.**ZeroPowerBehavior**.**BRAKE**);**

rightMotor**.**setZeroPowerBehavior**(**DcMotor**.**ZeroPowerBehavior**.**FLOAT**);**

Schimbarea acestei variabile va afecta cum se comporta DCmotorul , atat timp cat ii aplicam zeroPower.BRAKE o sa faca motorul sa incerce sa incetineasca daca acesta este în miscare, iar FLOAT face motorul sa alunece catre stop, lasand fricțiunea sa faca toata treaba. Este important sa tii cont de faptul ca valorile encoderului pot fi citite în oricare dintre aceste moduri disponibile, atat timp cat encoderul este conectat în modul corect. Aceste moduri doar schimba cum motor reactioneaza la aceste valori ale encoderului.

-> Vezi mai multe pe documentatie REV Robotics

ATENTIE

RUN_TO_POSITION poate fi o modalitate convenabilă de a controla un mecanism cu un singur motor, deoarece descarcă toate lucrările de control; totuși, deoarece fiecare motor este tratat în mod independent, este nerecomandabil să se utilizeze acest lucru pe mecanisme cu mai multe motoare, în special transmisii.

Encoders

Termen

note

Codificator

Un encoder se referă la un dispozitiv care urmărește (în general) mișcarea de rotație în jurul unei axe.

Există codificatori absoluti și relativi. Un encoder absolut va raporta exact în ce unghi este arborele în comparație cu „zeroul” său absolut. Un encoder relativ va raporta cât de mult s-a rotit arborele de când a început urmărirea (de exemplu, când pornește autonom). Codificatoarele relative vor avea o ieșire în cuadratura, în timp ce codificatoarele absolute au, în general, ieșiri analogice sau i2c.

Codificatoarele sunt folostie pentru a ajuta la gasire pozitie unde se afla robotul sau unul dintre mecanismele acestuia

Deși toate motoarele legale FTC conțin encodere în cuadratură relative încorporate, acestea trebuie să fie cablate separat și nu sunt necesare pentru utilizare. Codificatoarele externe pot fi utilizate și conectate la un port de codificare, atât timp cât utilizează protocolul de comunicare în cuadratură.

Accesarea encoderelor necesită apelarea unei metode pe obiectul DcMotor, getCurrentPosition(), care returnează poziția curentă a encoderului conectat la port. Acest număr poate fi arbitrar la începutul unui mod de operare și nu este resetat la 0 decât dacă se utilizează STOP_AND_RESET_ENCODERS sau dacă hub-ul de expansiune este alimentat ciclic.

tip

Nu există o terminologie standardizată reală atunci când este vorba de encodere în cuadratură. SDK utilizează în mod implicit „CPR” sau Contoare pe revoluție. De asemenea, este posibil ca unele fișe tehnice să indice „PPR” sau impulsuri pe revoluție. Un impuls poate fi echivalent cu 1 până la 4 „numărători” SDK. Fiți atenți atunci când citiți fișele tehnice!

danger

Encoderele cu un număr mare de numărători pe revoluție, cum ar fi Encoderul REV Through Bore, pot pierde pași dacă sunt conectate la porturile 1 sau 2. În plus, apelurile la getVelocity() pe un obiect DcMotorEx se pot supraîncărca în cazul codificatoarelor cu un număr mare de numere pe revoluție, din cauza faptului că numărul returnat este un număr întreg cu semn de 16 biți.

Servo

servo.java
Servo relicServo **=** hardwareMap**.**get**(**Servo**.**class**,** "Release Servo"**);**

După instanțierea unui Servo, există două funcții principale care pot fi apelate: setPosition() și getPosition().

servo.java
releaseServo.setPosition(0.75);

telemetry.addData("Release Servo Target", releaseServo.getPosition());

setPosition() stabilește poziția servo-ului. SDK va utiliza o buclă de control încorporată cu potențiometrul servomotorului pentru a conduce servomotorul la acea poziție și pentru a menține acea poziție. setPosition() primește un dublu între 0 și 1, unde 0 este limita inferioară de rotație a servomotorului și 1 este limita superioară de rotație a servomotorului. Toate valorile intermediare sunt direct proporționale, astfel încât 0,5 reprezintă mijlocul, 0,75 reprezintă 3/4 din înălțime, etc.

getPosition() nu returnează poziția curentă a servomotorului, ci poziția sa țintă curentă. Dacă o variabilă pentru poziția țintă curentă a servomotorului este stocată corespunzător, această funcție nu ar trebui să fie necesară niciodată.

Continuous Rotation Servo

CRServo intakeServo = hardwareMap.get(CRServo.class, „Intake Servo”);

Un CRServo are o metodă principală; setPower(). Aceasta funcționează foarte asemănător cu setPower() a lui DcMotor, ceea ce înseamnă că dacă îi treceți 0 se oprește, dacă îi treceți 1 merge înainte cu viteză maximă, dacă îi treceți -1 merge înapoi cu viteză maximă și tot ce se află între.

intakeServo.setPower(0.75);

Digital IO

DigitalChannel digitalDevice = hardwareMap.get(DigitalChannel.class, "digital device");

Un DigitalChannel are câteva metode principale. setMode() este utilizat pentru a seta portul ca port OUTPUT sau INPUT, getState() returnează starea curentă a portului (funcționează numai în modul INPUT), iar setState() stabilește starea portului (funcționează numai în modul OUTPUT)

Sfat

Porturile digitale pornesc implicit în modul INPUT

Pericol

Porturile digitale sunt trase în sus pentru a preveni plutirea. Acest lucru înseamnă că există o rezistență între port și 3,3 V, astfel încât portul să indice implicit HIGH atunci când nu este conectat nimic. Ca urmare, dispozitivele digitale TREBUIE să conecteze pinul digital la masă atunci când este închis, apoi să îl lase neconectat atunci când este deschis. Pentru comutatoarele de limită, acest lucru înseamnă conectarea unui fir la masă și a celuilalt la portul digital. Conectarea greșită (conectarea 3,3 V la portul digital) poate provoca instabilitate și poate cauza blocarea hub-ului de expansiune.

Analog Input

AnalogInput analogInput = hardwareMap.get(AnalogInput.class, "analog input");

O intrare analogică are o metodă principală: getVoltage() care este utilizată pentru a obține tensiunea curentă de intrare a portului

Notă

Deși getMaxVoltage() returnează 3,3v, porturile de intrare analogice ale hub-ului de expansiune și control pot suporta în siguranță până la 5v.

O notă privind viteza de apel hardware

Fiecare apel hardware pe care îl efectuați (fie că este vorba de setarea puterii unui motor, setarea unei poziții servo, citirea unei valori a unui encoder etc.) va dura aproximativ 3 milisecunde pentru a fi executat, cu excepția apelurilor I2C care pot dura mai mult de 7ms. Acest lucru se datorează faptului că, în spatele scenei, SDK poate fi nevoit să facă mai multe apeluri hardware pentru a efectua operațiunea I2C.

Notă

Atunci când utilizați un Control Hub, este posibil să observați timpi de apel hardware considerabil mai rapizi deoarece Control Hub utilizează o conexiune UART directă la placa Lynx în loc să treacă prin USB și un intermediar FTDI, așa cum se întâmplă atunci când utilizați un telefon.

Aceste perioade pot părea rapide, dar se adună rapid. Luați în considerare o buclă de control pentru a merge înainte timp de N numărători encoder, menținând în același timp direcția cu ajutorul IMU. Aceasta ar necesita 5 apeluri hardware normale (4 set power + 1 read encoder) și un apel I2C (IMU), ceea ce înseamnă că ciclul buclei ar dura aproximativ 22 ms pentru a fi executat și, astfel, ar funcționa la aproximativ 45 Hz.

Aceasta înseamnă că este esențial să reduceți la minimum numărul de apeluri hardware pe care le efectuați pentru a vă menține buclele de control care rulează rapid. De exemplu, nu citiți un senzor mai mult de o dată pe buclă. În schimb, citiți-l o singură dată și stocați valoarea într-o variabilă dacă trebuie să o utilizați din nou în alte momente ale aceluiași ciclu al buclei.

Utilizarea unui apel hardware de citire masivă poate ajuta la rezolvarea acestei probleme. O citire masivă durează 3 ms pentru a fi executată la fel ca orice alt apel hardware normal, dar returnează mult mai multe date. Pentru a putea utiliza citirile în bloc, trebuie să executați SDK v5.4 sau o versiune superioară. Consultați Bulk Reads pentru mai multe informații

Probleme comune

warning

Aveți grijă ce cod luați ca referință din această pagină! O parte din el este intenționat „buggy” pentru a demonstra potențiale erori ușor de făcut.

Excepții

Excepțiile sunt evenimente care apar în timpul execuției unui program, perturbând fluxul normal de instrucțiuni, utilizate la eroare sau probleme care apar în timpul execuției. O excepție poate fi rezolvata pentru a evita propagarea, altfel orice excepție care nu este gestionată va determina oprirea imediată a fluxului programului.

Unele tipuri comune de excepții includ:

  • 1-NullPointerException

Apare atunci când încercați să apelați o metodă sau să obțineți o proprietate a unui obiect de la o variabilă cu o valoare nulă, ceea ce înseamnă practic că variabila nu deține încă o valoare sau că valoarea nu există.

Această excepție este una dintre cele mai frecvente în FTC®, mai jos este un exemplu care aruncă o NullPointerException:

nullptr.java
public class CrashyOpMode extends OpMode {

// Această apelare a metodei "get" va arunca o NullPointerException.
//
// Valoarea variabilei "hardwareMap" este null în acest moment, din cauza
// modului în care SDK-ul este limitat să definească valoarea acestei variabile,
// valoarea sa este definită chiar înainte de apelarea metodei init() (sau runOpMode() în LinearOpModes).
Servo clawServo = hardwareMap.get(Servo.class, "claw");

@Override
public void init() {
// Această instrucțiune nu va fi niciodată atinsă din cauza
// NullPointerException aruncată, explicată mai sus,
// deoarece se întâmplă înainte de începerea execuției OpMode.
clawServo.setPosition(0.5);
}

// ...

}

Acest lucru poate fi rezolvat mutând definiția valorii variabilei „Servo” în metoda init (sau runOpMode() în LinearOpModes) după cum urmează:

nullptr.java
public class WorkingOpMode extends OpMode {

Servo clawServo = null;

@Override

public void init() {
// Acest lucru nu va genera NullPointerException deoarece valoarea variabilei
// „hardwareMap” este definită în acest moment, dar rețineți că
// metoda „get” va returna null dacă numele „claw” nu este
// configurat. (consultați secțiunea „Utilizarea SDK”)
clawServo = hardwareMap.get(Servo.class, „claw”);

// Această instrucțiune ar trebui să fie executată acum.
//
// Rețineți că, dacă servomotorul „claw” nu este configurat, valoarea returnată
// de către hardwareMap va fi nulă, așa cum s-a explicat anterior, prin urmare,
// o NullPointerException va fi generata aici dacă se întâmplă acest lucru.

clawServo.setPosition(0.5);
}

// ...

}
  • TargetPositionNotSetException

Acest tip de excepție este unul personalizat din SDK. Aceasta înseamnă că ați schimbat modul de funcționare al motorului în RUN_TO_POSITION înainte de a seta o poziție țintă:

Targeterr.java
// Aceasta va arunca o excepție „TargetPositionNotSetException” aici!

motor.setRunMode(DcMotor.RunMode.RUN_TO_POSITION);

// Și această afirmație nu va fi atinsă.

motor.setTargetPosition(1120);

Se rezolvă prin simpla schimbare a ordinii declarațiilor; setarea poziției țintă mai întâi, apoi schimbarea RunMode:

// Setarea mai întâi a poziției țintă

motor.setTargetPosition(1120);

// Apoi se schimbă RunMode

motor.setRunMode(DcMotor.RunMode.RUN_TO_POSITION);
  • ArithmeticException

    • Apare atunci când se efectuează orice operații aritmetice imposibile, cum ar fi împărțirea la zero:

arithmethicerr.java
int number = 128 / 0; // Aceasta va arunca o ArithmeticException!

Aceasta poate fi rezolvata prin includerea codului susceptibil de a arunca acest tip de excepție într-un try catch block:

arithmethicerr.java
int number; // Declararea variabilei în afara domeniului de aplicare

try {

// Acordarea unei valori care va declansa eventual o excepție aritmetică

număr = 128 / 0;

} catch (ArithmeticException e) {

// Facem ceva atunci când se întâmplă ArithmeticException.

// (Valoarea variabilei „number” va rămâne 0)

}
  • Interrupted Exception

Înseamnă că SDK a solicitat oprirea OpMode și este considerată parte a funcționării normale. O întrerupere înseamnă că thread-ului i s-a cerut să se încheie, așa că nu intrați în panică atunci când vedeți un spam din acestea în logcat!

Dacă apelați o metodă care poate declansa o InterruptedException (cum ar fi Thread.sleep()), aceasta ar trebui tratată astfel, cu sintaxa try catch menționată anterior:

interruptederr.java
try {

// Blocați timp de 500 de milisecunde

Thread.sleep(500);

} catch(InterruptedException e) {

// Spune thread-ului curent (OpMode) să

// încheie execuția cât mai curând posibil

Thread.currentThread().interrupt();

}

Rețineți că LinearOpMode conține deja o metodă abreviată sleep() care face acest lucru in "background". (Și nu ar trebui să utilizați sleep-uri în OpMode deoarece acestea sunt controlate mai strict. Citiți secțiunile următoare pentru informații suplimentare)

Modul în care SDK gestionează excepțiile

Cu excepția InterruptedExceptions și a altor cazuri speciale interne, care pur și simplu determină oprirea OpMode, SDK-ul FTC efectuează o rutină de „oprire de urgență” atunci când este declansata o excepție care nu este tratată corespunzător. Aceasta oprește OpMode și afișează pe ecran traseul complet al stacktrace-ului. Stacktrace poate fi vizualizat și prin Logcat atunci când se utilizează Android Studio.

note

Înainte de SDK 8.0, se afișa doar prima linie a erorii și era necesar să selectați „Restart Robot” din meniu înainte de a rula din nou un OpMode. ::

În general, este o idee bună să verificati toate OpModurile în mod extensiv înainte de orice meci oficial, deoarece aceste excepții sunt perturbatoare.

Blocat în start, loop, stop...

OpModes sunt programe strict controlate, în sensul că SDK le cere să mearga într-un anumit fel cu metodele init(), loop() etc. Dacă durează mai mult de un anumit timp (5 secunde, sau 900 de milisecunde în comenzile stop) executarea unei acțiuni în oricare dintre aceste metode, SDK va efectua rutina de „oprire de urgență” explicată anterior, cu mesajul de eroare „blocat în acțiune”.

stucky.java
public class StuckyOpMode extends OpMode {

// ...
@Override

public void loop() {

// Nu faceți acest lucru într-un OpMode iterativ normal!

// Aceasta va cauza o eroare „blocat în stop” după

// 5 secunde, deoarece OpModes Iterative nu ar trebui

// sa fie blocate de bucle sau de orice operație de lungă durată.

while(true) {

// ...

}

}

}

Dacă trebuie să executați orice fel de acțiune lungă în OpMode, o altă opțiune ar fi utilizarea unui LinearOpMode în schimb.

LinearOpMode sunt mai puțin stricte, deoarece metoda lor unică runOpMode() poate curge mai liber, dar tot trebuie să fie cooperative pentru a opri cererile. Luați următorul cod ca exemplu:

stuckylop.java
public class StuckyLinearOpMode extends LinearOpMode {



@Override

public void runOpMode() {

// Așteaptă ca driverul să apese PLAY pe DS

waitForStart();



while(true) {

// Falucruri la infinit

}

}

}

Acest cod nu este cooperant cu solicitările de oprire, deoarece bucla while nu are o condiție de ieșire pentru a coopera cu oprirea OpMode, prin urmare, acest cod va cauza o eroare „blocat în oprire” odată ce este oprit în Driver station.

Pentru a coopera cu oprirea OpMode, este necesar să se adauge o condiție opModeIsActive() sau !isStopRequested() la toate buclele de blocare executate în metoda runOpMode(). Consultați pagina LinearOpMode vs OpMode pentru mai multe informații despre aceste metode.

Un exemplu pentru un LinearOpMode cooperativ ar fi următorul:

cooplop.java
public class CooperativeLinearOpMode extends LinearOpMode {



@Override

public void runOpMode() {

while(someCondition && !isStopRequested()) {

// Fa ceva în timp ce „AltaConditie”

// este adevărată și OpMode nu este oprit.

}



// Așteaptă ca driverul să apese PLAY pe DS

waitForStart();



while(someOtherCondition && opModeIsActive()) {

// Faceți ceva în timp ce „someCondition” este adevărat

// și OpMode este în funcțiune (pornit și nu oprit).

}

}



}